/*
 * Copyright (c) 2019.  drakelee.base All rights reserved.
 */

package com.base.components.feign.configuration.tx.lcn.beans;

import com.base.components.common.util.HttpClientUtil;
import com.base.components.feign.configuration.tx.lcn.LcnTxTransactionConfigurations;
import com.codingapi.tx.config.service.TxManagerTxUrlService;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
import org.springframework.stereotype.Component;

import java.lang.invoke.MethodHandles;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

/**
 * TxManagerTxUrlServiceImpl
 *
 * @author <a href="drakelee1221@gmail.com">LiGeng</a>
 * @version 1.0.0, 2018-05-29 17:20
 * @since 4.1.0
 */
@Component
@ConditionalOnProperty(name = "base.rpc.tx.lcn.enable", havingValue = "true")
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService {
  private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
  private static final String URI = "/tx/manager/";
  private final AtomicLong lastCheckTime = new AtomicLong(0);
  private final AtomicReference<String> currentTxManagerHost = new AtomicReference<>();
  @Value("${base.rpc.tx.lcn.txManagerUrlCheckTimeLimit:30000}")
  private long checkTimeLimit = 30_000;
  @Value("${base.rpc.tx.lcn.txManagerUrlCheckRetry:3}")
  private int checkRetry = 3;
  @Value("${module-tx-manager}")
  private String txManagerServerName;
  @Autowired
  private CachingSpringLoadBalancerFactory cachingSpringLoadBalancerFactory;
  private volatile FeignLoadBalancer feignLoadBalancer;

  @Override
  public String getTxUrl() {
    if(LcnTxTransactionConfigurations.HAS_EXIT.get()){
      throw new RuntimeException("load txManager url error > server is exit");
    }
    //ek中负载遍历一个txManager实例
    try {
      return findTxManagerHost();
    } catch (Exception e) {
      logger.error("load txManager url error", e);
      return null;
    }
  }

  private String findTxManagerHost() {
    long current = System.currentTimeMillis();
    long last = lastCheckTime.get();
    if (current - last > checkTimeLimit) {
      boolean success = lastCheckTime.compareAndSet(last, current);
      if(!success){
        return findTxManagerHost();
      }
      String choose = chooseHost();
      if (choose == null) {
        lastCheckTime.set(0L);
      }else{
        currentTxManagerHost.set(choose);
      }
    }
    String uri = currentTxManagerHost.get();
    logger.debug("currentTxManagerHost = " + uri);
    return StringUtils.isBlank(uri) ? null : (uri + URI);
  }

  private ILoadBalancer getLoadBalancer(){
    if(feignLoadBalancer == null){
      feignLoadBalancer = cachingSpringLoadBalancerFactory.create(txManagerServerName);
    }
    return feignLoadBalancer.getLoadBalancer();
  }

  private String chooseHost(){
    for (int i = 0; i < checkRetry; i++) {
      Server server = getLoadBalancer().chooseServer(null);
      if(server != null){
        String hostPort = server.getHostPort();
        if(!hostPort.startsWith("http")){
          hostPort = "http://"+hostPort;
        }
        try {
          HttpClientUtil.get(hostPort + "/info");
          return hostPort;
        } catch (Exception ignore) {
        }
      }
    }
    return null;
  }
}
